home *** CD-ROM | disk | FTP | other *** search
/ Night Owl 6 / Night Owl's Shareware - PDSI-006 - Night Owl Corp (1990).iso / 016a / gofer221.zip / CH12 < prev    next >
Text File  |  1991-11-20  |  20KB  |  529 lines

  1.  
  2.  
  3. Introduction to Gofer         12. DIALOGUES: INPUT AND OUTPUT                   
  4.  
  5.  
  6. 12. DIALOGUES: INPUT AND OUTPUT
  7.  
  8. The Gofer system implements a subset of  the  facilities  for  programs
  9. involving I/O described in the Haskell report [5].  In particular, this
  10. makes it possible for Gofer programs to be run  interactively,  and  to
  11. make limited use of  text  files  for  both  reading  and  writing.   A
  12. significant factor in the design of the Haskell I/O facilities is  that
  13. it allows  the  use  of  such  programs  without  loss  of  referential
  14. transparency.
  15.  
  16. 12.1 Basic description
  17. ----------------------
  18. Programs using the I/O facilities in Gofer are modelled by functions of
  19. type Dialogue, defined by the type synonym:
  20.  
  21.     type Dialogue    =  [Response] -> [Request]
  22.  
  23. In other words, a Gofer program produces a list of output values,  each
  24. of which may be thought of as a request for some  particular  input  or
  25. output action, and obtains the corresponding list of  operating  system
  26. responses as its input.  Note that the input list of responses will  be
  27. evaluated lazily; i.e. we can ensure that we do not attempt  to  obtain
  28. the response to a given request until that request has been completed.
  29.  
  30. The current range of requests supported by Gofer is  described  by  the
  31. following datatype definition, taken from the standard prelude:
  32.  
  33.     data Request  =  -- file system requests:
  34.                     ReadFile      String         
  35.                   | WriteFile     String String
  36.                   | AppendFile    String String
  37.                      -- channel system requests:
  38.                   | ReadChan      String 
  39.                   | AppendChan    String String
  40.                      -- environment requests:
  41.                   | Echo          Bool
  42.  
  43. Each response is an element  of  the  type  defined  by  the  following
  44. datatype definition, using an auxiliary datatype IOError to describe  a
  45. variety of error conditions that may occur:
  46.  
  47.     data Response = Success
  48.                   | Str String 
  49.                   | Failure IOError
  50.  
  51.     data IOError  = WriteError   String
  52.                   | ReadError    String
  53.                   | SearchError  String
  54.                   | FormatError  String
  55.                   | OtherError   String
  56.  
  57. The following list describes the kind of  I/O  behaviour  specified  by
  58. each form of Request and indicates the possible  Response  values  that
  59. may be obtained in each case:
  60.  
  61.   o  ReadFile  string:  Read  contents  of  file  named  by   "string".
  62.  
  63.  
  64.                                       49
  65.  
  66.  
  67.  
  68.  
  69. Introduction to Gofer         12.1 Basic description                            
  70.  
  71.  
  72.      Possible responses to this request are:
  73.  
  74.        o  Str contents if the request is successful, where "contents" is
  75.           a string (evaluated lazily) containing the contents of the the
  76.           file specified by the ReadFile request.
  77.  
  78.        o  Failure (SearchError name) occurs if file  "name"  cannot  be
  79.           accessed.
  80.  
  81.        o  Failure (ReadError name) occurs if some  other  error  occurs
  82.           whilst opening the file "name".
  83.  
  84.   o  WriteFile name string:  Write  the  given  "string"  to  the  file
  85.      "name".  If the file does not already exist, it is created  before
  86.      attempting to write the value to file.  If the file already exists
  87.      then it will be truncated to zero length before the write  begins.
  88.      No response is obtained until the string argument has  been  fully
  89.      evaluated and its contents written to  file.   Possible  responses
  90.      are:
  91.  
  92.        o  Success if the write to file was completed successfully.
  93.  
  94.        o  Failure (WriteError msg) if  an  error  was  detected  whilst
  95.           trying to perform the output.  If the problem occurred whilst
  96.           attempting to open the specified file,  then  "msg"  contains
  97.           the   filename,   otherwise   it   contains    a    printable
  98.           representation of the evaluation error which occurred.
  99.  
  100.   o  AppendFile name string: Similar to the  WriteFile  request  except
  101.      that the value of the given "string" is  appended  onto  the  file
  102.      "name" if that file already exists.  The  responses  that  may  be
  103.      obtained from this request are the same as those for WriteFile.
  104.  
  105.   o  ReadChan name:  Read  from  the  input  stream  "name".  Note that
  106.      it is an error to attempt to read from the same channel more  than
  107.      once in the same program.  Possible responses are:
  108.  
  109.        o  Str contents if the request is successful,  where  "contents"
  110.           is  a  string  (evaluated  lazily)  containing  the  list  of
  111.           characters entered on the input stream.
  112.  
  113.        o  Failure (SearchError name) if the  named  channel  cannot  be
  114.           found.  The only input channel known to Gofer is the standard
  115.           input channel "stdin".  For convenience, the standard prelude
  116.           defines the variable stdin bound to this string.
  117.  
  118.        o  Failure (ReadError name) if a ReadChan request for the  named
  119.           channel has already been given by a previous request.
  120.  
  121.   o  AppendChan name string:  Output "string" on  channel  "name".   No
  122.      response is obtained until the string has been fully evaluated and
  123.      written to the named channel.  Possible responses are:
  124.  
  125.        o  Success if the append to channel was completed successfully.
  126.  
  127.        o  Failure (SearchError name) if the  named  channel  cannot  be
  128.  
  129.  
  130.                                       50
  131.  
  132.  
  133.  
  134.  
  135. Introduction to Gofer         12.1 Basic description                            
  136.  
  137.  
  138.           found.  The only output channels known to Gofer are "stdout",
  139.           "stderr" and "stdecho" (which is actually just  another  name
  140.           for  "stdout"  in  Gofer).   For  convenience,  the  standard
  141.           prelude defines variables stdout, stderr and stdecho bound to
  142.           the corresponding string values.
  143.  
  144.        o  Failure (WriteError msg)  if  an  error  is  detected  whilst
  145.           trying to perform the output.  The string  "msg"  contains  a
  146.           printable  representation  of  the  evaluation  error   which
  147.           occurred.
  148.  
  149.   o  Echo status: Set the echo status on  the  standard  input  channel
  150.      stdin to the given boolean value.  If the  echo  status  is  True,
  151.      then user input will be echoed onto the screen as they  are  typed
  152.      and the usual line editing facilities (such a backspace or delete)
  153.      provided by the host system can be used to edit the input lines as
  154.      they are entered.  If the echo status is  False,  then  individual
  155.      characters may be read from the standard input channel without any
  156.      echo or line editing features.
  157.  
  158.      Note that at most one Echo request can be used in a  program,  and
  159.      must precede any ReadChan request for stdin.  If  not  set  by  an
  160.      explicit Echo request, the echo status defaults to True.  Possible
  161.      responses are:
  162.  
  163.        o  Success if the request was completed successfully.
  164.  
  165.        o  Failure  (OtherError  msg)  if  the  request  could  not   be
  166.           completed either because a readChannel request for stdin  has
  167.           already been processed, or because a  previous  Echo  request
  168.           has already been given.  The  corresponding  values of  "msg"
  169.           are   "stdin already in use"   and    "repeated Echo request"
  170.           respectively.
  171.  
  172. A simple example of a program using these facilities to output a short
  173. message on the standard output stream is:
  174.  
  175.     helloWorld      :: Dialogue
  176.     helloWorld resps = [AppendChan stdout "hello, world"]
  177.  
  178. Any expression entered into Gofer of type "Dialogue" will be treated as
  179. a Gofer program using I/O and will be executed accordingly:
  180.  
  181.     ? helloWorld
  182.     hello, world
  183.     (1 reduction, 28 cells)
  184.     ?
  185.  
  186. Notice that without the explicit type declaration, the type that  would
  187. be inferred for for helloWorld would  be  a  ->  [Request],  and  hence
  188. helloWorld would not be executed as a Dialogue program.  This point can
  189. be illustrated using lambda expressions:
  190.  
  191.     ? \resps -> [AppendChan stdout "hello, world"]
  192.     v128
  193.     (1 reduction, 7 cells)
  194.  
  195.  
  196.                                       51
  197.  
  198.  
  199.  
  200.  
  201. Introduction to Gofer         12.1 Basic description                            
  202.  
  203.  
  204.     ? (\resps -> [AppendChan stdout "hello, world"]) :: Dialogue
  205.     hello, world
  206.  
  207.     (1 reduction, 28 cells)
  208.     ? 
  209.  
  210. In many cases the  structure  of  an  expression  is  enough  to  fully
  211. determine its type  as  Dialogue  (or  equivalently  as  [Response]  ->
  212. [Request]), in which case no explicit types are required to ensure that
  213. the expression is treated as a Gofer program using I/O:
  214.  
  215.     ? \~[Success] -> [AppendChan stdout "hello, world"]
  216.     hello, world
  217.     (1 reduction, 29 cells)
  218.     ?
  219.  
  220. Note the use of the  irrefutable  pattern  ~[Success]  for  the  lambda
  221. expression in the last  example;  without  this,  the  usual  rules  of
  222. pattern matching as described in section 9 would force Gofer to try and
  223. match the pattern [Success] against the list of responses,  before  the
  224. corresponding request had been produced:
  225.  
  226.     ? \ [Success] -> [AppendChan stdout "hello, world"]
  227.  
  228.     Aborting Dialogue:
  229.           {error "Attempt to read response before request complete"}
  230.     (50 reductions, 229 cells)
  231.     ?
  232.  
  233. The next example takes a single string a  parameter  and  displays  the
  234. contents of the corresponding file:
  235.  
  236.     showFile               :: String -> Dialogue 
  237.     showFile name ~(read:_) = [ReadFile name, AppendChan stdout result] 
  238.      where result = case read of Str contents -> contents 
  239.                                  Failure _    -> "Can't open " ++ name 
  240.  
  241. With a few modifications, we can  implement  a  similar  program  which
  242. prompts for, and reads, a filename from the  standard  input  and  then
  243. reads and displays the contents of that file as before.   This  program
  244. is based on a similar example in the Haskell report [5]:
  245.  
  246.     main ~(Success : ~(Str userInput : ~(r3 : _)))  
  247.       = [ AppendChan stdout "Please type a filename: ", 
  248.           ReadChan stdin, 
  249.           ReadFile name, 
  250.           AppendChan stdout (case r3 of Str contents -> contents
  251.                                         Failure _    -> "Can't open "
  252.                                                         ++ name)
  253.         ] where (name : _) = lines userInput
  254.  
  255.  
  256.  
  257.  
  258.  
  259.  
  260.  
  261.  
  262.                                       52
  263.  
  264.  
  265.  
  266.  
  267. Introduction to Gofer         12.2 Continuation style I/O                       
  268.  
  269.  
  270. 12.2 Continuation style I/O
  271. ---------------------------
  272. As an alternative to the `stream-based' approach to programs using  the
  273. I/O facilities in Gofer, the  standard  prelude  defines  a  family  of
  274. functions which enables such programs to be written in a `continuation'
  275. style.  The basic idea is to define a function  corresponding  to  each
  276. different kind of request, whose parameters include the values required
  277. to make the request together with two continuations.  The continuations
  278. are functions describing "what to do next", one of which is used if the
  279. request is successful, the other if the request fails.
  280.  
  281. As an example, the ReadFile request  is  represented  by  the  function
  282. "readFile" whose definition is equivalent to:
  283.  
  284.     readFile name fail succ ~(r:rs) = ReadFile name : rest rs
  285.      where rest = case r of Str s           -> succ s
  286.                             Failure ioerror -> fail ioerror
  287.  
  288. The first thing to happen  when  a  dialogue  expression  of  the  form
  289. "readFile name fail  succ"  is  evaluated  is  that  the  corresponding
  290. request "ReadFile name" is added to the list of I/O  requests.   A  new
  291. dialogue value "rest" is chosen,  depending  on  the  response  to  the
  292. ReadFile request, and the program continues by  passing  the  remaining
  293. part of the response list to "rest".  The functions "succ"  and  "fail"
  294. (called the success and failure  continuations  respectively)  describe
  295. the way in which the new dialogue "rest" is obtained.
  296.  
  297. The following example (edited a little to fit within the margins of this
  298. document) shows how the readFile function described above can be used to
  299. print the contents of a file called "test" on the display:
  300.  
  301.     ? readFile "test" (\ioerror resps -> [])
  302.                       (\s resps->[AppendChan stdout s])
  303.     This is a test message
  304.  
  305.     (4 reductions, 52 cells)
  306.     ?
  307.  
  308. The success continuation "(\s resps->[AppendChan stdout s])" used  here
  309. receives the contents of the file "test" in the the parameter  "s"  and
  310. uses an AppendChan request to output that string on  the  display.   As
  311. this example shows, the stream based approach of the  previous  section
  312. can be combined with the continuation based style of  I/O  without  any
  313. difficulty.  The failure continuation "(\ioerror resps -> [])"  ignores
  314. the error condition "ioerror" which caused  the  request  to  fail  and
  315. gives a dialogue which terminates immediately without any action.  For
  316. example, assuming that the file "Test" cannot be found:
  317.  
  318.     ? readFile "Test" (\ioerror resps -> [])
  319.                       (\s resps->[AppendChan stdout s])
  320.  
  321.     (4 reductions, 24 cells)
  322.     ?
  323.  
  324. In practice, it is  usually  a  good  idea  to  produce  some  kind  of
  325. diagnostic message when an error occurs:
  326.  
  327.  
  328.                                       53
  329.  
  330.  
  331.  
  332.  
  333. Introduction to Gofer         12.2 Continuation style I/O                       
  334.  
  335.  
  336.     ? readFile "Test"
  337.          (\ioerror resps -> [AppendChan stdout (show' ioerror)])
  338.          (\s resps       -> [AppendChan stdout s])
  339.     SearchError "Test"
  340.     (11 reductions, 59 cells)
  341.     ?
  342.  
  343. In each of the  examples  above,  the  failure  continuation  has  type
  344. "FailCont" as defined by the following type  synonym  in  the  standard
  345. prelude:
  346.  
  347.    type FailCont  =  IOError -> Dialogue
  348.  
  349. Similarly, the success continuation, which takes a string  representing
  350. an input string and produces a new Dialogue has type "StrCont":
  351.  
  352.     type StrCont  =  String -> Dialogue
  353.  
  354. A third kind of continuation is needed for those requests which  return
  355. a  response  of  the  form  "Success"  if  successful   (e.g.    output
  356. requests).  In this case the continuation is simply another dialogue:
  357.  
  358.     type SuccCont =  Dialogue
  359.  
  360. The following list  gives  the  type  of  each  of  the  six  functions
  361. corresponding to the six different kinds of I/O  request  described  in
  362. the previous section.  Full definitions for each of these functions are
  363. given in appendix B:
  364.  
  365.     readFile   :: String -> FailCont -> StrCont -> Dialogue
  366.     writeFile  :: String -> String -> FailCont -> SuccCont -> Dialogue
  367.     appendFile :: String -> String -> FailCont -> SuccCont -> Dialogue
  368.     readChan   :: String -> FailCont -> StrCont  -> Dialogue
  369.     appendChan :: String -> String -> FailCont -> SuccCont -> Dialogue
  370.     echo       :: Bool -> FailCont -> SuccCont -> Dialogue
  371.  
  372. As an illustration of the use of these functions, we show how  each  of
  373. the example programs from the previous section can be  rewritten  using
  374. the  continuation  based  style  of  I/O,  starting  with  the  program
  375. "helloWorld":
  376.  
  377.     helloWorld :: Dialogue
  378.     helloWorld  = appendChan stdout "hello, world" abort done
  379.  
  380. In this case, the explicit type declaration is  not  actually  required
  381. since the type of the expression is completely determined by  the  type
  382. of "appendChan".  The failure continuation "abort" is equivalent to the
  383. function "(\ioerror resps -> [])" described above  and  terminates  the
  384. program if an error occurs without any further action.   In  a  similar
  385. way, "done"  is  the  trivial  dialogue  which  terminates  immediately
  386. without any action.  Both of these values are defined in the standard
  387. prelude:
  388.  
  389.    done         :: Dialogue
  390.    done resps    = []
  391.  
  392.  
  393.  
  394.                                       54
  395.  
  396.  
  397.  
  398.  
  399. Introduction to Gofer         12.2 Continuation style I/O                       
  400.  
  401.  
  402.    abort        :: FailCont
  403.    abort ioerror = done
  404.  
  405. Using the same approach, the "showFile" and "main"  programs  from  the
  406. previous section are written as:
  407.  
  408.     showFile :: String -> Dialogue
  409.     showFile name
  410.      = readFile name (\ioerror -> appendChan stdout
  411.                                      ("Can't open " ++ name) abort done)
  412.                      (\contents-> appendChan stdout contents abort done)
  413.  
  414.     main :: Dialogue
  415.     main  = appendChan stdout "Please type a filename: " abort
  416.             (readChan stdin abort
  417.             (\userInput -> let (name : _) = lines userInput in
  418.              readFile name
  419.               (\ioerror  -> appendChan stdout ("Can't open " ++ name)
  420.                                 abort done)
  421.               (\contents -> appendChan stdout contents abort done)))
  422.  
  423.  
  424. 12.3 Interactive programs
  425. -------------------------
  426. One of the principal motivations for including facilities  for  I/O  in
  427. Gofer programs was to provide a way of using  interactive  programs  as
  428. described in [1].  An interactive program is represented by a  function
  429. of type String -> String mapping an input string of characters  entered
  430. at the keyboard into an output string to be displayed on the screen.
  431.  
  432. There are two functions defined in the standard prelude  which  can  be
  433. used to `execute' functions of this kind as interactive programs:
  434.  
  435.   o  "interact f" executes f::String->String as an interactive  program
  436.      with echo on.  This  means  that  characters  are  read  from  the
  437.      keyboard a line at a time.  The usual editing characters  such  as
  438.      backspace can be used to correct mistakes which are noticed before
  439.      the return key is pressed at the end  of  each  line.   The  input
  440.      stream can be terminated by typing an end of file character at the
  441.      beginning of a line:
  442.  
  443.          ? interact (map toUpper)
  444.          This text was entered using the interact function
  445.          THIS TEXT WAS ENTERED USING THE INTERACT FUNCTION
  446.          ^Z
  447.          (874 reductions, 1037 cells)
  448.          ?
  449.  
  450.   o  "run f" behaves like "interact f" except that echo is turned  off.
  451.      In this case, the only way of terminating the input stream without
  452.      reaching the end of the string produced  by  "f"  is  to  use  the
  453.      interrupt key:
  454.  
  455.          ? run (map toUpper)     
  456.          ALTHOUGH THIS IS ENTERED IN LOWER CASE, IT STILL
  457.          APPEARS IN UPPER CASE !
  458.  
  459.  
  460.                                       55
  461.  
  462.  
  463.  
  464.  
  465. Introduction to Gofer         12.3 Interactive programs                         
  466.  
  467.  
  468.          {Interrupted!}
  469.  
  470.          (1227 reductions, 1463 cells)
  471.          ?
  472.  
  473. [ASIDE: of these two functions, only "interact" is also included in the
  474. standard prelude for Haskell, although "run" may also  be  added  to  a
  475. Haskell system using the definition below.]
  476.  
  477. The definitions of "interact" and "run"  provide  further  examples  of
  478. Gofer programs using simple I/O facilities:
  479.  
  480.     interact        :: (String -> String) -> Dialogue
  481.     interact f       = readChan stdin abort
  482.                             (\s -> appendChan stdout (f s) abort done)
  483.  
  484.     run             :: (String -> String) -> Dialogue
  485.     run f            = echo False abort (interact f)
  486.  
  487. [EXERCISE for the interested reader:  construct alternative definitions
  488. for these functions using the stream based approach from section 12.1.]
  489.  
  490.  
  491.  
  492.  
  493.  
  494.  
  495.  
  496.  
  497.  
  498.  
  499.  
  500.  
  501.  
  502.  
  503.  
  504.  
  505.  
  506.  
  507.  
  508.  
  509.  
  510.  
  511.  
  512.  
  513.  
  514.  
  515.  
  516.  
  517.  
  518.  
  519.  
  520.  
  521.  
  522.  
  523.  
  524.  
  525.  
  526.                                       56
  527.  
  528.  
  529.